package com.sanri.tools.modules.minio.controller;

import com.sanri.tools.modules.core.dtos.DictDto;
import com.sanri.tools.modules.core.exception.ToolException;
import com.sanri.tools.modules.core.utils.StreamResponse;
import com.sanri.tools.modules.minio.service.MinioService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.csource.common.MyException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * minio 文件管理
 */
@Controller
@Slf4j
@RequestMapping("/minio")
@Validated
public class MinioStreamController {

    @Autowired
    private StreamResponse streamResponse;

    @Autowired
    private MinioService minioService;

    @PostMapping("/uploadFiles")
    @ResponseBody
    public List<DictDto<String>> uploadFiles(@NotBlank String connName, @NotBlank String bucketName , @NotBlank String prefix, @RequestParam("files") MultipartFile[] files) throws Exception {
        List<DictDto<String>> dictDtos = new ArrayList<>();
        for (MultipartFile file : files) {
            String objectName = minioService.uploadFiles(connName,bucketName,prefix, file);
            dictDtos.add(new DictDto<>(file.getOriginalFilename(),objectName));
        }
        return dictDtos;
    }

    @GetMapping("/preview")
    public void preview(@NotBlank String connName, @NotBlank String bucketName, @NotBlank String objectName, HttpServletResponse response) throws Exception {
        final byte[] bytes = minioService.loadImage(connName, bucketName, objectName);
        if (bytes == null){
            throw new ToolException("文件丢失");
        }
        streamResponse.preview(new ByteArrayInputStream(bytes), MediaType.APPLICATION_OCTET_STREAM,response);
    }

    @GetMapping("/download")
    public void download(@NotBlank String connName, @NotBlank String bucketName,@NotBlank String objectNames,HttpServletResponse response) throws IOException {
        final String[] split = StringUtils.split(objectNames, ',');
        final List<String> objectIdArray = Arrays.stream(split)
                .map(item -> StringUtils.split(item, '\n'))
                .flatMap(Arrays::stream)
                .map(StringUtils::trim)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(objectIdArray)) {
            log.warn("没有收到任何需要下载的文件");
            return ;
        }

        final String extension = objectIdArray.size() == 1 ? FilenameUtils.getExtension(objectIdArray.get(0)) : "zip";

        String downloadFilename = "download_"+objectIdArray.size()+"_"+ DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.format(System.currentTimeMillis()) + "."+ extension;

        streamResponse.preDownloadSetResponse(MediaType.APPLICATION_OCTET_STREAM,downloadFilename,response);
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        response.setHeader("filename", downloadFilename);

        // 单文件下载
        if (objectIdArray.size() == 1){
            final String partObjectName = objectIdArray.get(0);
            // 如果只有一个文件, 不用打包下载
            try {
                byte[] bytes = minioService.loadImage(connName, bucketName, partObjectName);
                if (bytes == null){
                    log.warn("文件丢失: {}",partObjectName);
                    throw new ToolException("文件丢失: "+ partObjectName);
                }
                response.getOutputStream().write(bytes);
            } catch (Exception e) {
                log.warn("文件异常: {}",partObjectName);
                throw new ToolException("文件异常,dfsId: "+ partObjectName);
            }
            return ;
        }

        // 多文件时打包下载
        ZipArchiveOutputStream archiveOutputStream = new ZipArchiveOutputStream(response.getOutputStream());
        for (String partObjectName : objectIdArray) {
            final String filename = StringUtils.substringAfterLast(partObjectName, "/");
            try {
                byte[] bytes = minioService.loadImage(connName, bucketName, partObjectName);
                if (bytes == null){
                    log.warn("objectName: {} 文件丢失", partObjectName);
                    ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(new File(partObjectName), filename+" 文件丢失");
                    archiveOutputStream.putArchiveEntry(zipArchiveEntry);
                    archiveOutputStream.closeArchiveEntry();
                    continue;
                }
                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(new File(partObjectName), filename);
                archiveOutputStream.putArchiveEntry(zipArchiveEntry);
                archiveOutputStream.write(bytes);
                archiveOutputStream.closeArchiveEntry();
            } catch (Exception e) {
                log.warn("objectName: {} 文件异常", partObjectName);
                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(new File(partObjectName), filename+" 文件异常");
                archiveOutputStream.putArchiveEntry(zipArchiveEntry);
                archiveOutputStream.closeArchiveEntry();
            }
        }

        if (archiveOutputStream != null) {
            archiveOutputStream.close();
        }
    }
}
